<cfcomponent  output="true">
	<cfset variables.instance.xmlDoc = ""/>
	<cfset variables.instance.debug = false />
	<cfset variables.instance.dataMap = arrayNew(1)/>
	
	<cffunction name="init" output="false" access="public" returntype="datamapper" hint="init">
       	<cfargument name="xmlPath" type="string" default="" hint="this is the relative path to the xml file" required="true">
		<cfif arguments.xmlPath NEQ "">
			<cfset configure(arguments.xmlPath) />
		</cfif>
		<cfreturn this/>
    </cffunction>
	
    <cffunction name="configure" output="true" access="public" returntype="any" hint="I prases the xml document and put it into a workable format">
		<cfargument name="xml" type="any" default="" hint="This can either be pure xml or the path to the xml file." required="true">
		<cfargument name="xmlSourceType" type="string" default="file" hint="This is to determine how to process the xml. Type file will look for a file with the given relative path. The type that you can send is xml and it will just process the xml" required="true">
		<cfset var local = structNew() />
		<cfif arguments.xmlSourceType EQ 'file'>
			<!--- read the xml file --->
			<cffile action="read" file="#expandPath(arguments.xmlPath)#" variable="variables.instance.xmldoc">
		<cfelse>
			<cfset variables.instance.xmldoc  = arguments.xml />
		</cfif>
		<!--- check to see if XML is valid --->
		<cfif NOT isXML(variables.instance.xmldoc)>
			<cfthrow type="Custom" message="Not a valid XML Document.">
			<cfreturn false>
		</cfif>
		<cfset setXmlDoc(XMLParse(variables.instance.xmldoc).xmlRoot) />
		<cfset local.cfg = getXMLDoc() />
		<cfloop from="1" to="#arrayLen(local.cfg.xmlChildren)#" index="local.idx">
			<cfswitch expression="#local.cfg.xmlChildren[local.idx].xmlName#">
				<cfcase value="datamap">
					<cfset configureColumns(local.cfg.xmlChildren[local.idx],'columns',local.idx)>
					<cfset configureSource(local.cfg.xmlChildren[local.idx],'source',local.idx)>
					<cfset configureTarget(local.cfg.xmlChildren[local.idx],'target',local.idx)>
				</cfcase>		
			</cfswitch>
		</cfloop>
			
		
    </cffunction>
	<!---    Date: 12/23/2007 Usage:  --->
    <cffunction name="getvariables" output="false" access="public" returntype="any" hint="">
    	
		<cfreturn variables>
    </cffunction>
	
    <cffunction name="getDataMap" access="public" output="false" returntype="any" hint="I get the datamap struct">
		
		<cfreturn variables.instance.dataMap />
    </cffunction>
	
    <cffunction name="getdebug" access="private" output="false" returntype="boolean" hint="I get the debug.">
       
		<cfreturn variables.instance.debug/>
    </cffunction>	
	
    <cffunction name="setdebug" access="public" output="false" returntype="void" hint="I turn debug on and off">
		<cfargument name="debug" default="false">
       <cfset variables.instance.debug = arguments.debug />
    </cffunction>
	
	<cffunction name="getDataMapEL" access="public" output="false" returntype="any" hint="I get an elemnet in the datamap struct.">
		<cfargument name="el" default="">
		<cfargument name="pos" default="">
		<cftry>
			<cfreturn variables.instance.dataMap[arguments.pos][arguments.el] />
			<cfcatch type="any">
       			<cfreturn false />
			</cfcatch>
		</cftry>
    </cffunction>

    <cffunction name="setDataMapEL" access="public" output="false" returntype="void" hint="I add an element to the datamap struct">
       	<cfargument name="el" type="any" required="true" />
		<cfargument name="pos" type="any" required="true" />
		<cfargument name="elVal" type="any" default="" >
        <cfset variables.instance.dataMap[arguments.pos][arguments.el] = arguments.elVal />
    </cffunction>
	
	<cffunction name="getxmldoc" access="public" output="false" returntype="any" hint="I get the xmldoc">
      
		<cfreturn variables.instance.xmldoc />
    </cffunction>

    <cffunction name="setxmldoc" access="public" output="false" returntype="void" hint="I set the xmldoc">
       <cfargument name="xmldoc" type="any" required="true" />
       <cfset variables.instance.xmldoc = arguments.xmldoc />
    </cffunction>
	
	<cffunction name="destory" access="public" output="false" returntype="void" hint="I destory the datamap struct">
		
		<cfset variables.instance.dataMap = "">
    </cffunction>
	
    <cffunction name="getPKPos" access="private" output="false" returntype="any" hint="I get the primary key position in the column array">
       	<cfargument name="colArr" default="">
		<cfset var local = structNew() />
		
		<cfloop from="1" to="#arrayLen(arguments.colArr)#" index="local.idx">
			<cfif arguments.colArr[local.idx].targetisPk is true>
				<cfreturn local.idx/>
			</cfif>
		</cfloop>
		<cfreturn false/>
    </cffunction>
	
	<cffunction name="configureColumns" access="public" output="false" returntype="void" hint="I put all of the column information into a struct inside an array">
		<cfargument name="configXML" default="" required="true" >
		<cfargument name="dataStructName" default="" required="true" >
		<cfargument name="dataStructPos" default="" required="true" >
		<cfset var local = structNew() />		
		<cfset local['columns'] = arraynew(1) />
		<cfset local['temp'] = configXML.columnmap.xmlChildren />
		<cfloop from="1" to="#arrayLen(local['temp'])#" index="local.idx">			
			<cfset local['columns'][local.idx]['datatype'] = local.temp[local.idx].XmlAttributes.datatype />
			<cfset local['columns'][local.idx]['source'] = local.temp[local.idx].XmlAttributes.source />
			<cfset local['columns'][local.idx]['target'] = local.temp[local.idx].XmlAttributes.target />
			<cfif NOT StructKeyExists(local.temp[local.idx].XmlAttributes,'targetIsPK')>
				<cfset local['columns'][local.idx]['targetIsPK'] = false />
			<cfelse>
				<cfset local['columns'][local.idx]['targetIsPK'] = local.temp[local.idx].XmlAttributes.targetIsPK />
			</cfif>
			<cfif NOT StructKeyExists(local.temp[local.idx].XmlAttributes,'cfvariable')>
				<cfset local['columns'][local.idx]['cfvariable'] = '' />
			<cfelse>
				
				<cfset local['columns'][local.idx]['cfvariable'] = local.temp[local.idx].XmlAttributes.cfVariable />
				<cfset local.cfvarTemp = local.temp[local.idx].XmlChildren />
				<cfif arrayLen(local.cfvarTemp) GT 0>
					<cfset local['columns'][local.idx]['cfcustomCode'] = local.cfvarTemp[1].XmlText />					
				</cfif>
			</cfif>
		</cfloop>
		<cfset setDataMapEL(dataStructName,dataStructPos,local['columns']) />
	</cffunction>
	
	<cffunction name="configureSource" access="public" output="true" returntype="void" hint="I set up the from datasource information into a struct.">
		<cfargument name="configXML" default="" required="true" >
		<cfargument name="dataStructName" default="" required="true" >
		<cfargument name="dataStructPos" default="" required="true" >
		<cfset var local = structNew() />		
		<cfset local['source'] = structNew() />
		<cfset local['temp'] = configXML.source.xmlChildren />
		<cfloop from="1" to="#arrayLen(local['temp'])#" index="local.idx">			
			<cfset local['source'][local.temp[local.idx].XmlName] = local.temp[local.idx].XmlText />
		</cfloop>
		<cfset setDataMapEL(dataStructName,dataStructPos,local['source']) />
	</cffunction>
	
	<cffunction name="configureTarget" access="public" output="false" returntype="void" hint="I set up the to datasource information into a struct">
		<cfargument name="configXML" default="" required="true" >
		<cfargument name="dataStructName" default="" required="true" >
		<cfargument name="dataStructPos" default="" required="true" >
		<cfset var local = structNew() />		
		<cfset local['target'] = structNew() />
		<cfset local['temp'] = configXML.target.xmlChildren />
		<cfloop from="1" to="#arrayLen(local['temp'])#" index="local.idx">			
			<cfset local['target'][local.temp[local.idx].XmlName] = local.temp[local.idx].XmlText />
		</cfloop>
		<cfset setDataMapEL(dataStructName,dataStructPos,local['target']) />
	</cffunction>

	<cffunction name="processMap" output="false" access="public" returntype="boolean">
		<cfset var selectFrom = false />
		<cfset var tempQ = false />
		<cfset var insertTo = false />
		<cfset var returnVal = false />
		<cfset var local = structNew() />
		
		<cfloop from="1" to="#ArrayLen(getDataMap())#" index="local.pidx">
			<!--- Check to see if the source has a datasource --->
			<cfif NOT structKeyExists(getDataMapEl('source',local.pidx),'datasource')>
				<cfthrow type="custom" message="There is no datasource for the from database.">
				<cfreturn false/>
			</cfif>
			<!--- Check to see if the source has a table to select from  --->
			<cfif NOT  structKeyExists(getDataMapEl('source',local.pidx),'tablename')>
				<cfthrow type="custom" message="There is no tablename for the source database.">
				<cfreturn false/>
			</cfif>		
			<!--- Check to see if the from source has at least one columen --->
			<cfif NOT isArray(getDataMapEl('columns',local.pidx))>
				<cfthrow type="custom" message="There are no columns for the from database.">
				<cfreturn false/>
			<cfelse>
				<cfset local.columns = getDataMapEl('columns',local.pidx) />
				<cfset local.targetColumns="">
				<cfset local.sourceColumns="">
				<!--- Build a local list of the from columns and of the to columns --->
				<cfloop from="1" to="#arraylen(local.columns)#" index="local.idx">
					<cfif NOT ListFindNoCase(local.sourceColumns, local.columns[local.idx].source)>
						<cfset local.sourceColumns = ListAppend(local.sourceColumns, local.columns[local.idx].source) />				
					</cfif>					
					<cfif NOT ListFindNoCase(local.targetColumns, local.columns[local.idx].target)>
						<cfset local.targetColumns = ListAppend(local.targetColumns, local.columns[local.idx].target) />				
					</cfif>
				</cfloop>
				<!--- check to see if any of the from columns are ColdFusion variables --->
				<cfif listfindnocase(local.sourceColumns,'$${cfvar}',',')>
					<cfset local.sourceColumns = listDeleteAt(local.sourceColumns,listfindnocase(local.sourceColumns,'$${cfvar}',','))>
				</cfif>
			</cfif>
			<cfset setDataMapEl('local',local.pidx,getDataMapEl('source',local.pidx))>
			
			<cfif StructKeyExists(getDataMapEl('source',local.pidx),'customCode')>
				<cfset processCustomCode(cfCode=getDataMapEl('source',local.pidx).customCode)/>			
			</cfif>
			<!--- <cftry> --->	
				<!--- Try and select data from the from source --->
				<cfquery name="selectFrom" datasource="#getDataMapEl('source',local.pidx).datasource#" 
						username="#iif(structKeyExists(getDataMapEl('source',local.pidx),'username'),de(getDataMapEl('source',local.pidx).username),'')#" 
						password="#iif(structKeyExists(getDataMapEl('source',local.pidx),'password'),de(getDataMapEl('source',local.pidx).password),'')#">
					SELECT 
						<cfif ListLen(local.sourceColumns)>
						#local.sourceColumns#
						<cfelse>
						*
						</cfif>
					FROM #getDataMapEl('source',local.pidx).tablename#		
					<cfif StructKeyExists(getDataMapEl('source',local.pidx),'condition')>
						#getDataMapEl('source',local.pidx).condition#
					</cfif>
				</cfquery>
				<cfif getDebug() is true>
					<cfset setDataMapEl('selectFrom',local.pidx,selectFrom)>
				</cfif>
				<!--- <cfcatch type="database">
					<cfthrow type="custom" message="There was a problem selecting the data.">
				</cfcatch> --->
			<!--- </cftry> --->
			<!--- <cfdump var="#local.columns#"> --->
			<!--- If there is data loop through --->
			<cfloop query="selectFrom">
				<!--- check to see if there is a primary key --->
				<cfif getPKPos(local.columns) neq false>
					<!--- Loop through the column array to find custom code to be ran --->
					<cfloop from="1" to="#arrayLen(local.columns)#" index="local.idx">
						<cfif structKeyExists(local.columns[getPKPos(local.columns)],"cfvariable") AND 
								structKeyExists(local.columns[getPKPos(local.columns)],"cfcustomCode")>
							<cfif local.columns[getPKPos(local.columns)].cfvariable NEQ '' AND 
								local.columns[getPKPos(local.columns)].cfcustomCode NEQ '' >
								<cfset local.customField = true />
							</cfif>
						</cfif>
					</cfloop>
					<!--- If there is no custom code to be ran select from the from source to make sure we're not entering a dupilcate key --->
					<cfif NOT StructKeyExists(local,'customField')>
						<cfquery name="local.tempQ" datasource="#getDataMapEl('target',local.pidx).datasource#" 
							username="#iif(structKeyExists(getDataMapEl('target',local.pidx),'username'),de(getDataMapEl('target',local.pidx).username),'')#" 
							password="#iif(structKeyExists(getDataMapEl('target',local.pidx),'password'),de(getDataMapEl('target',local.pidx).password),'')#">
							SELECT 
								#local.targetColumns#
							FROM #getDataMapEl('target',local.pidx).tablename#		
							WHERE #local.columns[getPKPos(local.columns)].target# = 
							<cfqueryparam cfsqltype="#local.columns[getPKPos(local.columns)].datatype#" value="#selectFrom[local.columns[getPKPos(local.columns)].source][currentrow]#">						
						</cfquery>
					</cfif>

					<cfif StructKeyExists(local,'customField') OR local.tempQ.recordCount is 0>
						<cfquery name="insertTo" datasource="#getDataMapEl('target',local.pidx).datasource#" 
								username="#iif(structKeyExists(getDataMapEl('target',local.pidx),'username'),de(getDataMapEl('target',local.pidx).username),'')#" 
								password="#iif(structKeyExists(getDataMapEl('target',local.pidx),'password'),de(getDataMapEl('target',local.pidx).password),'')#">
							INSERT INTO #getDataMapEl('target',local.pidx).tablename# (#local.targetColumns#)
							VALUES(
								<!--- Loop through the column array to get the data from the correct columns --->
								<cfloop from="1" to="#arrayLen(local.columns)#" index="local.idx">
									<!--- Check for custom ColdFusion code to be ran --->
									<cfif structKeyExists(local.columns[local.idx],"cfvariable") AND StructKeyExists(local.columns[local.idx],"cfcustomCode")>
										<cfif local.columns[local.idx].cfvariable NEQ '' AND local.columns[local.idx].cfcustomCode NEQ '' >
											<cfqueryparam cfsqltype="#local.columns[local.idx].datatype#" value="#processCustomCode(local.columns[local.idx].cfvariable,local.columns[local.idx].cfcustomCode)#">
										<cfelse>
											<cfqueryparam cfsqltype="#local.columns[local.idx].datatype#" value="#selectFrom[local.columns[local.idx].source][currentrow]#">
										</cfif>
									<cfelse>
										<cfqueryparam cfsqltype="#local.columns[local.idx].datatype#" value="#selectFrom[local.columns[local.idx].source][currentrow]#">
									</cfif>
								<cfif local.idx NEQ arrayLen(local.columns)>, </cfif>
								</cfloop>
							)
						</cfquery>
						<cfif structkeyexists(local,'customfield')>
							<!--- kill the struct key customField --->
							<cfset structdelete(local,'custumfield')>
						</cfif>
					</cfif>
				<cfelse>
					<cfquery name="insertTo" datasource="#getDataMapEl('target',local.pidx).datasource#" 
							username="#iif(structKeyExists(getDataMapEl('target',local.pidx),'username'),de(getDataMapEl('target',local.pidx).username),'')#" 
							password="#iif(structKeyExists(getDataMapEl('target',local.pidx),'password'),de(getDataMapEl('target',local.pidx).password),'')#">
						INSERT INTO #getDataMapEl('target',local.pidx).tablename# (#local.targetColumns#)
						VALUES(
							<!--- Loop through the column array to get the data from the correct columns --->
							<cfloop from="1" to="#arrayLen(local.columns)#" index="local.idx">
								<!--- Check for custom ColdFusion code to be ran --->
								<cfif structKeyExists(local.columns[local.idx],"cfvariable") AND StructKeyExists(local.columns[local.idx],"cfcustomCode")>
									<cfif local.columns[local.idx].cfvariable NEQ '' AND local.columns[local.idx].cfcustomCode NEQ '' >																	
										<cfqueryparam cfsqltype="#local.columns[local.idx].datatype#" value="#processCustomCode(local.columns[local.idx].cfvariable,local.columns[local.idx].cfcustomCode)#">
									<cfelse>
										<cfqueryparam cfsqltype="#local.columns[local.idx].datatype#" value="#selectFrom[local.columns[local.idx].source][currentrow]#">
									</cfif>
								<cfelse>
									<cfqueryparam cfsqltype="#local.columns[local.idx].datatype#" value="#selectFrom[local.columns[local.idx].source][currentrow]#">
								</cfif>							
							<cfif local.idx NEQ arrayLen(local.columns)>, </cfif>
							</cfloop>
						)
					</cfquery>
				</cfif>
			</cfloop>
			<cfif StructKeyExists(getDataMapEl('target',local.pidx),'customCode')>
				<cfset processCustomCode(cfCode=getDataMapEl('target',local.pidx).customCode)/>			
			</cfif>
			<cfif getDebug() is true>
				<cfset setDataMapEl('local',local.pidx,local)>
			</cfif>
		</cfloop>
		
		<cfreturn true>
	</cffunction>
	
	<!---    Date: 12/19/2007 Usage: I process custom ColdFusion code --->
    <cffunction name="processCustomCode" output="false" access="public" returntype="any" hint="I process custom ColdFusion code">
    	<cfargument default="" name="cfVar">
		<cfargument default="" name="cfCode">
		<cfset var local = structNew() />
		<!--- declare local variables --->
		<!--- create the temp file name --->
		<cfset local.custTemplate = createuuid() & '.cfm' />
		<!--- get the path of where we are going to store the file --->
		<cfset local.filepath = getdirectoryfrompath(getcurrenttemplatepath()) & local.custTemplate />
		<!--- set up the temp function name --->
		<cfset local.functionName = "cfcustomFunction" & createUUID() />
		<cfset local.functionName = rereplacenocase(local.functionName,'-','','all')>
		<!--- start building the temp code --->
		<cfset local.code = arguments.cfCode & chr(10) & chr(13) />
		<cfset local.code = arguments.cfCode &"<cfreturn #arguments.cfvar#/>" />
		<cfset local.return = "" />	
		<cftry>
			<cflock name="#local.filePath#" type="exclusive" timeout="30">
				<!--- Create temp coldfusion function  --->
				<cfset local.code = '<' & 'cffunction name="#local.functionName#">#local.code#</' & 'cffunction>' />
				<!--- save the temp function to a temp file  --->
				<cffile action="write" file="#local.filePath#" output="#local.code#" />
				<!--- include the temp file --->
				<cfinclude template="#local.custTemplate#" />
				<!--- run function --->
				<cfset local.return = evaluate("#local.functionName#()")>
				<!--- delete temp file --->
				<cffile action="delete" file="#local.filePath#" />
			</cflock>
			
			<cfcatch type="any">
			</cfcatch>
		</cftry>
		<!--- delete the temp function --->
		<cfif StructKeyExists(variables,local.functionName)>
			<cfset StructDelete(variables,local.functionName)>
		</cfif>
		<cfreturn local.return/>
    </cffunction>
</cfcomponent>